package com.lcc.lynx.security.advice;

import com.lcc.lynx.common.ResultVO;
import com.lcc.lynx.common.utils.JsonUtil;
import com.lcc.lynx.common.utils.SpringUtil;
import com.lcc.lynx.security.log.IApiLogRecord;
import com.lcc.lynx.security.log.LogProperties;
import com.lcc.lynx.security.wrapper.RepeatedlyRequestWrapper;
import jakarta.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.Order;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import java.util.*;

@Order(1)
@RestControllerAdvice
public class ApiLogAdvice implements ResponseBodyAdvice<ResultVO<?>> {

    private static final Logger logger = LoggerFactory.getLogger(ApiLogAdvice.class);

    @Autowired
    private IApiLogRecord apiLogRecord;

    @Autowired
    private LogProperties logProperties;

    List<String> excludeHeaders = List.of("host,connection,content-length,pragma,cache-control,sec-ch-ua-platform,user-agent,accept,sec-ch-ua,sec-ch-ua-mobile,sec-fetch-site,sec-fetch-mode,sec-fetch-dest,referer,accept-encoding,accept-language".split(","));


    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        return returnType.getParameterType() == ResultVO.class;
    }

    /**
     * 处理 controller 方法的返回值
     * @param body 方法返回对象
     * @param returnType 对象类型
     * @param selectedContentType the content type selected through content negotiation
     * @param selectedConverterType the converter type selected to write to the response
     * @param request the current request
     * @param response the current response
     * @return
     */
    @Override
    public ResultVO beforeBodyWrite(ResultVO<?> body, MethodParameter returnType, MediaType selectedContentType,
                                    Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        HttpServletRequest req = SpringUtil.getRequest();
        req.setAttribute("_respBody_", body);
        String uri = req.getServletPath();
        for (String not : logProperties.getExcludes()) {
            int i = not.indexOf("*");
            if(i != -1){
                if(uri.startsWith(not.substring(0, i-1))){
                    return body;
                }
            }
            if(uri.equals(not)){
                return body;
            }
        }
        try{
            if(req instanceof RepeatedlyRequestWrapper reqWrapper){
                String requestBody = reqWrapper.getBody();
                String responseBody = JsonUtil.toJSONString(body);
                String method = req.getMethod();
                String contentType = req.getContentType();
                Enumeration<String> headerNames = req.getHeaderNames();
                Map<String, String> headers = new HashMap<>();
                // 排除默认header参数列表
                while (headerNames.hasMoreElements()) {
                    String headerName = headerNames.nextElement();
                    if(excludeHeaders.contains(headerName)){
                        continue;
                    }
                    headers.put(headerName, req.getHeader(headerName));
                }
                String headerJson = JsonUtil.toJSONString(headers);
                apiLogRecord.record(reqWrapper.getStartTime(), requestBody, responseBody,
                        req.getRequestURI(), headerJson, method, contentType);
            }

        }catch (Exception e){
            logger.error("记录接口日志异常", e);
        }
        return body;
    }

}
